/*
 * Flota.cpp
 *
 *  Created on: 09/01/2010
 *      Author: San Martín, Matías I.
 */

#include "Flota.h"

#include <cstdlib>

#include "../../../Framework/Common/File.h"

namespace aplicacion {

Flota::Flota(const IdFlota& id, int cantidadNaves, T_SEMILLA semilla,
		double escalaDistribucion,
		const Posicion& posPartida, const Posicion& posDestino,
		T_TIEMPO tiempoActual)
	: 	_identificador(id),
		_tahora(tiempoActual),
		_destino(posDestino)
{
	this->generarNaves(posPartida,posDestino,cantidadNaves,semilla, escalaDistribucion);
}


void Flota::cambiarTiempo(T_TIEMPO tiempo)
{
	_tahora = tiempo;
}

/* FIXME LA GENERACION DE POSICIONES NO RESPONDE A UNA DISTRIBUCION NORMAL
 * COMO SE INDICA EN LA DOCUMENTACION */
void Flota::generarNaves(const Posicion& posPartida, const Posicion& posDestino,
				int cantidadNaves, T_SEMILLA semilla, double escalaDistribucion)
{
	srand(semilla);

	T_TIEMPO ultimoArribo = 0;

	for (;cantidadNaves--;)
	{
		t_coordenada x = posPartida.getX() +
					//se distribuye entre x +- la mitad de la escala de distribucion
					( ( ((double)rand()) / RAND_MAX) - 0.5) * escalaDistribucion;

		t_coordenada y = posPartida.getY() +
						/* se distribuye con un extra aleatorio con distribucion
						 * exponencial cuya media es escalaDistribucion/4 */
						this->randExponencial(escalaDistribucion/4);

		/* TODO HACER ROTACION DE FORMA TAL QUE EL EJE Y CORRESPONDA A LA
		 * DIRECCION DE DESPLAZAMIENTO DE LA FLOTA (DIRECCION ENTRE PLANETA
		 * ORIGEN Y DESTINO) */

		//las coordenadas no pueden ser negativas
		x = (x < 0)? 0:x;
		y = (y < 0)? 0:y;
		Posicion posNave(x,y);
		Nave nuevaNave(posNave);

		/* inserto la nave segun orden de llegada */
		T_TIEMPO tiempoArribo = nuevaNave.getTiempoArribo(posDestino);
		if (ultimoArribo <= tiempoArribo)
		{
			ultimoArribo = tiempoArribo;
			//la nave llega despues de la ultima agregada, se agrega al final
			_naves.push_back(nuevaNave);
		}else{

			//se busca donde se inserta
			bool faltaInsertar = true;
			t_naves::iterator fin = _naves.end();
			for (t_naves::iterator i = _naves.begin();
					faltaInsertar && i != fin;
					i++)
			{
				/* se inserta antes de la primer nave con un tiempo de arribo
				 * mayor o igual */
				if (tiempoArribo <= i->getTiempoArribo(posDestino) )
				{
					faltaInsertar = false;
					_naves.insert(i,nuevaNave);
				}
			}
		} //insercion de nave
	}//ciclo de creacion

	this->actualizarTamanio();
}

Flota::Flota(const IdFlota& id)
	: _identificador (id)
{

}

Flota::Flota()
{
}

Flota::~Flota() {
}

size_t Flota::setTiempo(T_TIEMPO tiempo)
{
	size_t cantidadArribaron = 0;

	T_TIEMPO tiempoTranscurrido = tiempo - _tahora;
	t_naves::iterator fin = _naves.end();
	t_naves::iterator ultimaArribo = _naves.begin();

	//recorro las naves para actualizar su estado
	for (t_naves::iterator i = ultimaArribo; i != fin; ++i) {

		//defino si la nave ya arribo al destino
		if (i->getTiempoArribo(_destino) <= tiempoTranscurrido)
		{
			cantidadArribaron++;
			ultimaArribo = i;
		}else{
			//si la nave no arribo todavia, actualizo su estado
			i->moverADestino(_destino,tiempoTranscurrido);
		}
	}

	//si arribo alguna nave
	if (cantidadArribaron)
	{
		//elimino de la coleccion las naves que arribaron
		_naves.erase(_naves.begin(), ++ultimaArribo);

		this->actualizarTamanio();
	}

	//actualizo el tiempo
	_tahora = tiempo;

	return cantidadArribaron;
}

size_t Flota::arribaran(T_TIEMPO tiempo) const
{
	if (tiempo <= _tahora)
		return 0;

	//en tiempo guardo la duracion hasta el momento indicado
	tiempo -= _tahora;

	size_t arribaran = 0;
	t_naves::const_iterator fin = _naves.end();
	t_naves::const_iterator iter = _naves.begin();

	//cuento la cantida de naves cuyo tiempo de arribo es menor o igual que el indicado
	while (iter != fin && iter->getTiempoArribo(_destino) <= tiempo)
	{
		iter++;
		arribaran++;
	}

	return arribaran;

}

bool Flota::operator == (const IdFlota& id) const
{
	return _identificador == id;
}

const IdFlota& Flota::getId() const
{
	return _identificador;
}

void Flota::setId(const IdFlota& id)
{
	_identificador = id;
}

//T_SEMILLA Flota::getSemillaCreacion() const
//{
//	return _semilla;
//}

/**
 * Devuelve las naves de la flota ordenadas por tiempo (creciente)
 * hasta el tiempo indicado (inclusive).
 */
std::list<const Nave*>* Flota::getNavesPorTiempoHasta(T_TIEMPO tiempoHasta) const
{
	std::list<const Nave*>* retNaves = new std::list<const Nave*>();

	/* si no hay una diferencia positiva entre el tiempo indicado y el actual se
	 * el resultado sera una lista vacia (no arribara ninguna nave) */
	if (tiempoHasta <= _tahora)
		return retNaves;

	T_TIEMPO tfalta = tiempoHasta - _tahora;
	t_naves::const_iterator fin = _naves.end();
	for (t_naves::const_iterator i = _naves.begin();
			//se agregan al resultado las naves que llegaran hasta el tiempoHasta
			i != fin && i->getTiempoArribo(_destino) <= tfalta;
			i++)
	{
		retNaves->push_back(&(*i));
	}

	return retNaves;
}

/**
 * Devuelve las naves de la flota ordenadas por tiempo (creciente)
 * hasta la cantidad de naves indicada.
 */
std::list<const Nave*>* Flota::getNavesPorTiempoHastaCantidad(size_t cantidadHasta) const
{
	std::list<const Nave*>* retNaves = new std::list<const Nave*>();

	size_t cantidadAgregada = 0;
	t_naves::const_iterator fin = _naves.end();
	for (t_naves::const_iterator i = _naves.begin();
			//se agregan al resultado las naves que llegaran hasta el tiempoHasta
			i != fin && cantidadAgregada < cantidadHasta;
			i++)
	{
		retNaves->push_back(&(*i));
		cantidadAgregada++;
	}

	return retNaves;
}

size_t Flota::save (int fd) const
{
	size_t guardar = 0;

	guardar += sizeof(_tahora);
	File::writeAll(fd,&_tahora,sizeof(_tahora));

	guardar += _destino.save(fd);

	size_t cantNaves = _naves.size();
	guardar += sizeof(cantNaves);
	File::writeAll(fd,&cantNaves,sizeof(cantNaves));

	//guardo las naves
	t_naves::const_iterator fin = _naves.end();
	for (t_naves::const_iterator i = _naves.begin(); i != fin; i++ )
	{
		guardar += i->save(fd);
	}

	return guardar;
}

size_t Flota::load (int fd)
{
	size_t cargar = 0;
	_naves.clear();

	cargar += sizeof(_tahora);
	File::readAll(fd,&_tahora,sizeof(_tahora));

	cargar += _destino.load(fd);

	size_t cantNaves;
	cargar += sizeof(cantNaves);
	File::readAll(fd,&cantNaves,sizeof(cantNaves));

	//cargo las naves
	while(cantNaves--)
	{
		Nave nave;
		cargar += nave.load(fd);
		_naves.push_back(nave);
	}

	this->setTamanio(cargar);

	return cargar;
}

/**
 * @see middleware::io::SerializableToArray#saveToArray(int)
 */
size_t Flota::saveToArray (uint8_t* arr) const {
	uint8_t* arr_original = arr;

	memcpy(arr, &_tahora, sizeof(_tahora));
	arr += sizeof(_tahora);

	arr += _destino.saveToArray(arr);

	size_t cantNaves = _naves.size();
	memcpy(arr, &cantNaves, sizeof(cantNaves));
	arr += sizeof(cantNaves);

	//guardo las naves
	t_naves::const_iterator fin = _naves.end();
	for (t_naves::const_iterator i = _naves.begin(); i != fin; i++ )
	{
		arr += i->saveToArray(arr);
	}

	return arr - arr_original;
}

/**
 * @see middleware::io::SerializableToArray#loadFromArray(int)
 */
size_t Flota::loadFromArray (const uint8_t* arr) {
	const uint8_t* arr_original = arr;

	_naves.clear();

	memcpy(&_tahora, arr, sizeof(_tahora));
	arr += sizeof(_tahora);

	arr += _destino.loadFromArray(arr);

	size_t cantNaves;
	memcpy(&cantNaves, arr, sizeof(cantNaves));
	arr += sizeof(cantNaves);

	//cargo las naves
	while(cantNaves--)
	{
		Nave nave;
		_naves.push_back(nave);
		arr += nave.loadFromArray(arr);
	}

	size_t cargar = arr - arr_original;
	this->setTamanio(cargar);

	return cargar;
}

void Flota::actualizarTamanio()
{
	Nave nave;
	this->setTamanio( sizeof(_tahora) +
					_destino.getTamanio() +
					sizeof(size_t) +
					_naves.size() * nave.getTamanio());
}

} //namespace aplicacion
